其他错误处理策略
原来的错误处理代码比采用异常之后的代码更短,也更优美。但那却是通过程序各部分之间的紧密耦合而得到的。那种方式不能很好地用到由分别开发的库组合而成的程序中。
我们也可以考虑去掉专门的错误处理函数skip(),方式是在main()里引进一个状态变量。例如,
int main(int argc, char* argv[]) // 糟糕风格的例子
{
// ...
bool in_error = false;
while(*Driver::input) {
try {
Lexer::get_token();
if(Lexer::curr_tok == Lexer::END) break;
if(Lexer::curr_tok == Lexer::PRINT) {
in_error = false;
continue;
}
if(in_error == false) cout << Parser::expr(false) << '\n';
}
catch (Error::Zero_divide) {
cerr << "attempt to divide by zero\n";
in_error = true;
}
catch (Error::Syntax_error e) {
cerr << "syntax error: " << e.p << '\n';
in_error = true;
}
}
if(Driver::input != &std::cin) delete Driver::input;
return Driver::no_of_errors;
}
我认为这是一个很坏的办法,因为:
1)、状态变量是混乱和错误的一个常见根源,特别是允许它们大量出现并影响程序中较大的片段时。特别地,我认为使用in_error的main()版本不如使用skip()的版本好读。
2)、一般说,使处理错误的代码与“正常”代码分离是一种很好的策略。
3)、在导致错误的代码的同一个抽象层次上处理错误是非常危险的。完成错误处理的代码有可能又产生了引起错误处理的那个错误。我把下面问题留做练习:请说明在使用in_error的main()版本中呢怎么会出现这种情况(8.5[7])。
4)、修改“正常”的代码,加上错误处理代码,所需的工作量比增加单独的错误处理例行程序更多。
异常处理的意图是去处理非局部的问题。如果一个错误可以在局部处理,那么(几乎)总应该这样做。例如,完全没有必要对“过多参数错误”使用异常机制:
int main(int argc, char* argv[])
{
using namespace std;
using namespace Driver;
switch (argc) {
case 1:
input = &cin;
break;
case 2:
input = new istringstream(argv[1]);
break;
default:
cerr << "too many arguments\n";
return 1;
}
// 如前
}
第14章将进一步讨论有关异常的问题。
🔚